package lol.clann.minecraft.springboot.api.model.menu;

import com.ruoshui.utils.java.annotation.Nonnull;
import com.ruoshui.utils.java.exception.ExceptionUtils;
import com.ruoshui.utils.java.random.RandomUtils;
import lol.clann.minecraft.springboot.api.bukkit.spi.callback.IconClickCallback;
import lol.clann.minecraft.springboot.api.bukkit.utils.MenuUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.ServerUtils;
import lol.clann.minecraft.springboot.api.model.CacheItem;
import lol.clann.minecraft.springboot.api.service.CacheService;
import lombok.Getter;
import lombok.ToString;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: zyp
 * @date: 2020/11/2 3:08 下午
 */
@ToString
public class Icon implements Cloneable {
    @Getter
    private final long uid; // 会设置到图标的nbt里. 点击时读取uid,就能找到对应的Icon
    private final MenuUtils menuService;
    @Getter
    private final Menu menu;
    private final CacheService cacheService;
    private final ServerUtils serverUtils;
    private final Set<String> executors = Collections.newSetFromMap(new ConcurrentHashMap<>());
    @Getter
    private MenuClickConcurrentLevelEnum concurrentLevel; // 并发等级 如果不显示指定,就是用Menu的并发等级
    @Getter
    private boolean async; // 异步执行
    @Getter
    private ItemStack realItem; // 设置进来的物品
    @Getter
    private long cooldown; // 冷却时间 ms
    private IconClickCallback callback;
    @Getter
    private String showPermission; // 看见图标所需的权限
    @Getter
    private String clickPermission; // 点击图标所需的权限
    @Getter
    int index;

    @Getter
    private ItemStack showIcon; // 图标

    Icon(Menu menu, ItemStack realItem) {
        this.uid = RandomUtils.nextLong();
        this.menu = menu;
        this.menuService = menu.getMenuService();
        if (realItem == null) {
            throw new IllegalArgumentException("参数缺失 realItem");
        }
        this.realItem = realItem;
        this.showIcon = realItem.clone();
        menuService.setUid(showIcon, uid);
        serverUtils = menuService.getServerUtils();
        cacheService = menuService.getCacheService();
    }

    /**
     * 设置到gui
     *
     * @param realItem
     */
    public void setItem(@Nonnull ItemStack realItem) {
        if (realItem == null) {
            throw new RuntimeException("icon can not be null");
        }
        this.realItem = realItem;
        this.showIcon = realItem.clone();
        menuService.setUid(this.showIcon, uid);
        menu.setIcon(index, this);
    }

    /**
     * 移动到指定位置
     * 若目标位置有图标,会覆盖
     *
     * @param menuIndex 全局索引
     */
    public void moveTo(int menuIndex) {
        menu.setIcon(index, null);
        menu.setIcon(menuIndex, this);
    }

    /**
     * 移动到本页的指定位置
     *
     * @param pageIndex 页内索引
     */
    public void moveToInSagePage(int pageIndex) {
        moveTo(getPage() * menu.getPageSize() + pageIndex);
    }

    /**
     * 复制到指定位置
     *
     * @param menuIndex
     */
    public void copyTo(int menuIndex) {
        Icon icon = this.clone();
        menu.setIcon(menuIndex, icon);
    }

    public void copyToInSagePage(int pageIndex) {
        copyTo(getPage() * menu.getPageSize() + pageIndex);
    }

    /**
     * 页码
     *
     * @return
     */
    public int getPage() {
        return index / menu.getPageSize();
    }

    void onClick(Player player, InventoryClickEvent event) {
        if (callback == null) {
            return;
        }
        if (!isClickable(player)) {
            return;
        }
        executors.add(player.getName());
        if (async) {
            serverUtils.runTaskAsynchronously(() -> {
                doClick(player, event);
            });
        } else {
            doClick(player, event);
        }
    }

    private void doClick(Player player, InventoryClickEvent event) {
        try {
            callback.onClick(this, player, event);
        } catch (Throwable throwable) {
            ExceptionUtils.throwException(throwable);
        } finally {
            executors.remove(player.getName());
        }
    }

    private boolean isClickable(Player player) {
//        权限检查
        if (clickPermission != null && !clickPermission.isEmpty() && !player.hasPermission(clickPermission)) {
            player.sendMessage(ChatColor.RED + "权限不足");
            return false;
        }
        String clickKey = ("sys.menu.icon.click.cooldown." + getUid()).intern();
//        点击冷却
        if (this.cooldown > 0) {
            CacheItem<Object> item = cacheService.getCacheItem(clickKey);
            if (item != null) {
                long cooldown = item.getExpireAfterCreate() / 1000000 - (System.currentTimeMillis() - item.getCreated());
                player.sendMessage(ChatColor.RED + "操作过于频繁,请于" + (cooldown) + "毫秒后再次尝试!");
                return false;
            }
            item = new CacheItem();
            item.setExpireAfterCreate(cooldown * 1000000);
            cacheService.putCacheItem(clickKey, item);
        }
//            并发控制
        if (concurrentLevel == MenuClickConcurrentLevelEnum.ALL) {
            if (!executors.isEmpty()) {
                player.sendMessage(ChatColor.RED + "您或者其他玩家的点击操作尚未结束,请稍后再试!");
                return false;
            }
        } else if (concurrentLevel == MenuClickConcurrentLevelEnum.CLICKER) {
            if (executors.contains(player.getName())) {
                player.sendMessage(ChatColor.RED + "您的点击操作尚未结束,请稍后再试!");
                return false;
            }
        }
        return true;
    }

    public boolean canShow(Player player) {
        return showPermission == null || showPermission.isEmpty() || player.hasPermission(showPermission);
    }


    @Getter
    public static class IconBuilder {
        private final Menu menu;
        private ItemStack realItem;
        private int index;
        private MenuClickConcurrentLevelEnum concurrentLevel;
        private boolean async;
        private long cooldown;
        private IconClickCallback callback;
        private String showPermission;
        private String clickPermission;

        IconBuilder(Menu menu) {
            this.menu = menu;
        }

        public IconBuilder concurrentLevel(MenuClickConcurrentLevelEnum concurrentLevel) {
            this.concurrentLevel = concurrentLevel;
            return this;
        }

        public IconBuilder showPermission(String showPermission) {
            this.showPermission = showPermission;
            return this;
        }

        public IconBuilder clickPermission(String clickPermission) {
            this.clickPermission = clickPermission;
            return this;
        }

        public IconBuilder async(boolean async) {
            this.async = async;
            return this;
        }

        public IconBuilder callback(IconClickCallback callback) {
            this.callback = callback;
            return this;
        }

        public IconBuilder icon(ItemStack icon) {
            this.realItem = icon;
            return this;
        }

        public IconBuilder cooldown(long cooldown) {
            this.cooldown = cooldown;
            return this;
        }

        public IconBuilder index(int index) {
            this.index = index;
            return this;
        }

        /**
         * 构建icon,并设置到menu上
         *
         * @return
         */
        public Icon build() {
            Icon icon = new Icon(menu, realItem);
            icon.index = index;
            icon.concurrentLevel = concurrentLevel;
            icon.async = async;
            icon.cooldown = cooldown;
            icon.callback = callback;
            icon.showPermission = showPermission;
            icon.clickPermission = clickPermission;
            menu.setIcon(this.index, icon);
            return icon;
        }
    }

    @Override
    public Icon clone() {
        Icon icon = new Icon(menu, realItem);
        icon.index = index;
        icon.concurrentLevel = concurrentLevel;
        icon.async = async;
        icon.cooldown = cooldown;
        icon.callback = callback;
        icon.showPermission = showPermission;
        icon.clickPermission = clickPermission;
        return icon;
    }
}
